import { AudioClip, AudioSource, Node, director, find } from "cc";
import ResManager from "./ResManager";

const EFFECT_MAX_NUM = 5;
class AudioManager {
    private static instance: AudioManager;
    public static get Ins() {
        if (!this.instance) this.instance = new AudioManager();
        return this.instance
    }
    private constructor() {
    }
    public static clear() {
        this.instance = null;
    }

    private _audioSourceMusic: AudioSource;
    private _playingAudios: AudioSource[] = [];
    private _audios: AudioSource[] = [];

    init() {
        let parentNode = new Node("audioSource")
        director.addPersistRootNode(parentNode)
        this._audioSourceMusic = this.createSourceNode("audioSourceMusic", parentNode)
        for (let i = 0; i < EFFECT_MAX_NUM; i++) {
            this._audios.push(this.createSourceNode("audioSourceSound" + i, parentNode))
        }
        // EventManager.on(GameEvent.CHANNEL_ON_SHOW, this.resumeMusic, this)
        // EventManager.on(GameEvent.CHANNEL_ON_HIDE, this.pauseMusic, this)
    }

    private createSourceNode(name: string, parentNode: Node): AudioSource {
        let audioSourceNode = new Node(name);
        audioSourceNode.parent = parentNode
        // director.addPersistRootNode(audioSourceMusic);
        return audioSourceNode.addComponent(AudioSource);
    }

    /* 播放背景音乐 */
    private musicPath: string
    private isMusicLoading = false
    async playMusic(path: string) {
        this.musicPath = path

        this.isMusicLoading = true
        let audioClip: AudioClip = await this.getAudioClip(path);
        this.isMusicLoading = false
        if (this.isMusicStopAfterLoading) {
            this.isMusicStopAfterLoading = false
            // 音乐在加载期间，调用了停止，加载完毕后，不播放了
            return
        }

        if (this._audioSourceMusic.clip) {
            this._audioSourceMusic.stop();
        }
        this._audioSourceMusic.clip = audioClip;
        this._audioSourceMusic.loop = true;
        this._audioSourceMusic.play()
    }

    /* 停止播放背景音乐 */
    private isMusicStopAfterLoading = false
    stopMusic() {
        this._audioSourceMusic.stop()
        if (this.isMusicLoading) {
            this.isMusicStopAfterLoading = true
        }
    }

    pauseMusic() {
        this._audioSourceMusic.pause()
    }

    async resumeMusic() {
        if (this.musicPath) {
            let audioClip: AudioClip = await this.getAudioClip(this.musicPath);
            this._audioSourceMusic.clip = audioClip;
            this._audioSourceMusic.loop = true;
            this._audioSourceMusic.play()
        }
    }

    /* 背景音乐是否正在播放 */
    isMusicPlaying(): boolean {
        return this._audioSourceMusic.playing
    }

    private GetAudioSourceFormPool(): AudioSource {
        // for (let i = 0; i < this._playingAudios.length; i++) {
        //     if (!this._playingAudios[i].playing) {
        //         return this._playingAudios[i];
        //     }
        // }
        // let parentNode = find("audioSource");
        // let ret: AudioSource = null;
        // /**需要扩容 */
        // this._playingAudios.push(
        //     ret = this.createSourceNode("audioSourceSound" + (this._playingAudios.length), parentNode)
        // )
        // return ret;
        let audio = this._audios.pop();
        if (!audio) audio = this.createSourceNode("audioSourceSound" + (this._playingAudios.length), find("audioSource"));
        return audio;
    }

    /* 播放音效 */
    async playSound(path: string, callback?: Function, loop = false) {
        let audioClip: AudioClip = await this.getAudioClip(path);
        if (audioClip) {
            let audioSource = this.GetAudioSourceFormPool();
            audioSource.clip = audioClip;
            audioSource.loop = loop;
            audioSource.play()
            audioSource.node.targetOff(AudioSource.EventType.ENDED);
            this._playingAudios.push(audioSource);
            audioSource.node.once(AudioSource.EventType.ENDED, () => {
                if (callback) callback();
                this._audios.push(audioSource);
                for (let i = 0; i < this._playingAudios.length; i++) {
                    if (this._playingAudios[i] == audioSource) {
                        this._playingAudios.splice(i, 1);
                        break;
                    }
                }
            }, this);
        }
    }

    pauseSound(path: string) {
        for (let i = 0; i < this._playingAudios.length; i++) {
            if (this._playingAudios[i].clip?.nativeUrl === path) {
                this._playingAudios[i].pause()
                break
            }
        }
    }

    stopSound(path: string) {
        for (let i = 0; i < this._playingAudios.length; i++) {
            if (this._playingAudios[i].clip?.nativeUrl === path) {
                this._playingAudios[i].stop();
                this._playingAudios[i].node.targetOff(AudioSource.EventType.ENDED);
                this._audios.push(this._playingAudios.splice(i, 1)[0]);
                break
            }
        }
    }

    resumeSound(path: string) {
        for (let i = 0; i < this._playingAudios.length; i++) {
            if (this._playingAudios[i].clip?.nativeUrl === path && this._playingAudios[i].state === AudioSource.AudioState.PAUSED) {
                this._playingAudios[i].play()
                break
            }
        }
    }

    /* 停止播放所有音效 */
    stopAllSound() {
        this._playingAudios.forEach(audioSource => {
            audioSource.stop();
            audioSource.node.targetOff(AudioSource.EventType.ENDED);
            this._audios.push(audioSource);
        })
        this._playingAudios = [];
    }

    resumeAllSound() {
        this._playingAudios.forEach(audioSource => {
            if (audioSource.state === AudioSource.AudioState.PAUSED) {
                audioSource.play()
            }
        })
    }


    private async getAudioClip(path: string) {
        let clip: AudioClip = ResManager.Ins.get<AudioClip>(path, AudioClip);
        if (!clip) {
            if (path.indexOf('http') != -1) {
                clip = await ResManager.Ins.loadRemote<AudioClip>(path);
            } else {
                clip = await ResManager.Ins.load<AudioClip>(path, AudioClip);
            }
        }
        return clip;
    }

    isSoundPlaying(name: string) {
        this._playingAudios.forEach((e) => {
            if (e.clip.name = name) {
                return true;
            }
        })
        return false;
    }

    release(path: string) {
        ResManager.Ins.release(path);
    }
}

export default AudioManager;